/**
 * @author bbroome
 */
new Test.Unit.Runner(
{
  testObjectNotPolluted: function() 
  {
    var iterations = 0, obj = { a: 1, b: 2, c: 3 };
    
    for (property in obj) 
      iterations++;
      
    this.assertEqual(3, iterations, "Object shouldn't be polluted");
  },
  
  test$A: function() 
  {
    // All of the follwoing should return an empty array
    this.assertEnumEqual([], _C.A([]), "empty array (1)");
    this.assertEnumEqual([], _C.A()  , "empty array (2)");
    this.assertEnumEqual([], _C.A({}), "empty array (3)");
    this.assertEnumEqual([], _C.A(5) , "empty array (4)");
    
    function getArgumentArray()
    {
      return _C.A(arguments);
    }
    
    this.assertEqual(typeof [], typeof getArgumentArray(),
      "arguments are turned into an actual array");
    this.assertEnumEqual([], getArgumentArray(),
      "the Array of arguments we expect is returned (1)");
    this.assertEnumEqual(['foo'], getArgumentArray('foo'),
      "the Array of arguments we expect is returned (2)");
    this.assertEnumEqual(['foo','bar'], getArgumentArray('foo','bar'),
      "the Array of arguments we expect is returned (3)");
    this.assertEnumEqual(['foo',22,undefined], getArgumentArray('foo',22,undefined),
      "the Array of arguments we expect is returned (4)");
    
    // Make sure this works with NodeLists, whether directly in HTML...
    var a = _C.A($('testNode').childNodes);
    this.assertEqual(3, a.length,
      "Length of arrays created from NodeLists returned from queries");
    this.assertEqual("22", a[0].textContent,
      "Content of arrays created from NodeLists with text nodes");
    this.assertEqual($('testChildNode'), a[1],
      "Content of arrays created from NodeLists with child nodes");
    
    // ...or created via the DOM
    var element = document.createElement('div');
    element.appendChild(document.createTextNode('22'));
    element.appendChild(document.createElement('span'));
    this.assertEqual(2, $A(element.childNodes).length,
      "Array creation from NodeLists created via the DOM");
    
    // ...or created via an HTML string
    element = document.createElement('div');
    $(element).update('22<span></span><span></span>');
    this.assertEqual(3, $A(element.childNodes).length,
      "Array creation from NodeLists created via HTML injection");
  },
  
  test$addMethods: function() 
  {
    var aClass = function() {}, 
        e      = { mood: function() { return "Happy"; } },
        f      = { nonMethod: 'f' },
        g      = { mood: function() { return "Sad"; } };
        
    // only methods should be added
    _C.addMethods(aClass, f);
    this.assertUndefined(aClass.nonMethod, 
      "aClass shouldn't have a nonMethod property"); 
    this.assertUndefined(new aClass().nonMethod, 
      "objects of aClass shouldn't have a nonMethod property"); 
    
    // ensure methods are actually added
    _C.addMethods(aClass, e);
    this.assertUndefined(aClass.mood, 
      "the constructor shouldn't have a mood method");
    this.assertEqual("Happy", new aClass().mood(), 
      "objects of aClass should have a mood method");
    
    // ensure methods are not overwritten
    _C.addMethods(aClass, g);
    this.assertEqual("Happy", new aClass().mood(), 
      "aClass objects should still have a happy mood"); 
    
    // ...unless we specify they should be
    _C.addMethods(aClass, g, true);
    this.assertEqual("Sad", new aClass().mood(), 
      "aClass objects should now have a sad mood"); 
  },
  
  test$isA: function() 
  {
    this.assertFalse(_C.isA(null, Object), "null is no Object");
    this.assertFalse(_C.isA(undefined, Object), "undefined is no Object");
    this.assertTrue(_C.isA({}, Object), "{} is an Object");
    
    this.assertFalse(_C.isA([], Object), "[] is no Object");
    this.assertTrue(_C.isA([], Array), "[] is an Array");
    
    this.assertFalse(_C.isA('string', Object), "'string' is no Object");
    this.assertTrue(_C.isA('string', String), "'string is a String");
    
    this.assertFalse(_C.isA(5, Object), "5 is no Object");
    this.assertTrue(_C.isA(5, Number), "5 is a Number");
    this.assertTrue(_C.isA(0, Number), "0 is a Number");
    
    this.assertFalse(_C.isA(true, Object), "true is no Object");
    this.assertTrue(_C.isA(true, Boolean), "true is a Boolean");
    this.assertTrue(_C.isA(false, Boolean), "false is a Boolean");
    
    this.assertFalse(_C.isA(function() {}, Object), "function is no Object");
    this.assertTrue(_C.isA(function() {}, Function), "function is a Function");
    
    this.assertFalse(_C.isA(new Date(), Object), "new Date() is no Object");
    this.assertTrue(_C.isA(new Date(), Date), "new Date() is a Date");
    
    var NewClass = function() {}, instance = new NewClass();
    this.assertFalse(_C.isA(instance, Object), "instance is no Object");
    this.assertTrue(_C.isA(instance, NewClass), "instance is a NewClass");
  },
  
  test$newClass: function()
  {
    var ClassA = _C.newClass({}),
        ClassB = _C.newClass({ 
          superclass: ClassA,
          sayIt: function(message) { 
            return message; 
          },
          jump : function() { 
            return 'How high?'; 
          }
        }),
        ClassC = _C.newClass({ 
          superclass: ClassB,
          construct : function(height) { 
            this.height = height; 
          },
          jump      : function() { 
            return 'Right away! ' + this.height + ' feet.'; 
          },
          getCoffee : function() { 
            return 'Two creams?'; 
          }
        }),
        ClassD = _C.newClass({ 
          superclass: ClassC, 
          construct : function($super, height) { 
            $super(height + 3); 
          },
          sayIt     : function($super, message) { 
            return 'You expect me to say "' + 
              $super(message) + '"? Not happening... oh, wait.'; 
          }
        }),
        ClassE = _C.newClass({ 
          superclass: ClassD, 
          construct : function($super, height) { 
            $super(height);
          }
        }),
        instanceA = new ClassA(),
        instanceB = new ClassB(),
        instanceC = new ClassC(5),
        instanceC2 = new ClassC(3),
        instanceD = new ClassD(5),
        instanceE = new ClassE(5);
      
     this.assertIdentical(instanceA.constructor, ClassA,
       "instanceA was constructed by ClassA");  
     this.assertIdentical(instanceB.constructor, ClassB,
       "instanceB was constructed by ClassB");  
     this.assertIdentical(instanceC.constructor, ClassC,
       "instanceC was constructed by ClassC");         
     this.assertIdentical(instanceD.constructor, ClassD,
       "instanceD was constructed by ClassD");  
       
     this.assertDefined(ClassA.addMethods, 
       "addMethods should exist in the class");        
     this.assertUndefined(ClassA.construct, 
       "construct should exist in instances, not the class");   
     this.assertDefined(instanceA.construct, 
       "construct should exist in instances");     
     this.assertUndefined(instanceA.addMethods, 
       "addMethods should exist in the class, not in instances");   
           
     // Make sure our isA function is working correctly with our classes
     this.assertTrue(_C.isA(instanceA, Object), 'instanceA is a Object');
     this.assertTrue(_C.isA(instanceA, ClassA), 'instanceA is a ClassA');
     this.assertFalse(_C.isA(instanceA, ClassB), 'instanceA is no ClassB');
     this.assertTrue(_C.isA(instanceB, ClassB), 'instanceB is a ClassB');
     this.assertFalse(_C.isA(instanceB, String), 'instanceB is no String');     
     this.assertTrue(_C.isA(instanceB, ClassA), 'instanceB is a ClassA'); 
     this.assertTrue(_C.isA(instanceC, ClassA), 'instanceC is a ClassA'); 
     this.assertTrue(_C.isA(instanceC, ClassB), 'instanceC is a ClassB'); 
     this.assertTrue(_C.isA(instanceD, ClassB), 'instanceD is a ClassB');
     this.assertTrue(_C.isA(instanceE, ClassD), 'instanceE is a ClassD');
     
     // Make sure that subclasses aren't polluting their ancestors' prototypes
     this.assertUndefined(instanceA.sayIt, "instanceA shouldn't have sayIt");
     this.assertUndefined(instanceB.getCoffee, "instanceB shouldn't have getCoffee");
     
     // Now make sure that inheritance is working correctly
     this.assertEqual("you're daft", instanceB.sayIt("you're daft"), 
       "instanceB sayIt should echo");
     this.assertEqual("you're daft", instanceC.sayIt("you're daft"), 
       "instanceC sayIt should echo");
     this.assertEqual("You expect me to say \"you're daft\"? Not happening... oh, wait.", 
       instanceD.sayIt("you're daft"), "instanceD sayIt should give lip");
       
     this.assertEqual('How high?', instanceB.jump(), 
       "instanceB should ask how high to jump");
     this.assertEqual('Right away! 5 feet.', instanceC.jump(), 
       "instanceC should jump the number of feet passed to its constructor");
     this.assertEqual('Right away! 3 feet.', instanceC2.jump(), 
       "instanceC2 should jump the number of feet passed to its constructor");
     this.assertEqual('Right away! 8 feet.', instanceD.jump(), 
       "instanceD should jump the number of feet passed to its constructor + 3");
     this.assertEqual('Right away! 8 feet.', instanceE.jump(), 
       "instanceE should jump the number of feet passed to its constructor + 3");
     
     this.assertEqual('Two creams?', instanceD.getCoffee(), 
       "instanceD should ask how you take your coffee");
  }
});